package nodex

import (
	"context"
	"fmt"
	"gitee.com/zhongguo168a/go-nodex/logx"
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/reflectx"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"zhongguo168a.top/mycodes/gocodes/utils/stringutil"
)

// CallMode 调用模式
type CallMode int

const (
	// GOROUTINE 新建协程
	// 每次请求都新建一个协程处理
	// 避免阻塞读取消息的协程
	// 通常用于没有队列需求的独立的请求，例如玩家登陆，验证等
	GOROUTINE CallMode = iota
	// CHANRPC 使用chanrpc服务器，即协程级的rpc服务器
	// 使用[CHANRPC]模式，需要创建并指定[ChanRPCSelector]
	// 一个rpc服务器相当于一个消息盒子，读取消息后投递到选择器返回的rpc服务器
	// 避免阻塞读取消息的协程
	// 通常用于处理玩家，行会等有顺序要求的请求。
	CHANRPC
	// CONNECTION 连接的消息队列
	// 在用户连接的读取消息的协程中处理请求完成后，继续读取消息
	// 会阻塞消息的读取
	// 通常用于有队列需求的，数量较少的请求，否则会阻塞读取，避免读缓存过大导致异常
	CONNECTION
)

type RouterConfig struct {
	// 添加到的网关
	AtServers []string
	// 路由的路径
	// 格式为：group/message
	Path string
	// 调用模式
	CallMode CallMode
	// 使用CHANRPC模式时，指定的选择器
	ChanRPC string
	// 上下文创建
	ContextCreator ContextCreator
	// 处理器
	Handler reflect.Value
	//
	EnableHttpGet bool
}

func (n *NodeConfig) AddRouterList(routers []*RouterConfig) {
	for _, val := range routers {
		n.AddRouter(val)
	}
}

func (n *NodeConfig) AddRouter(router *RouterConfig) {
	n.Routes = append(n.Routes, router)
}

// Router 以路由的形式，响应客户端的请求
type Router struct {
	Config *RouterConfig
	// 参数的反射类型
	Args reflect.Type
	// 参数中的返回值
	// 返回值的反射类型
	ArgsReply reflect.Type
	// 只有0和1两种情况
	// 0表示不返回任何数据, 1-表示返回一个错误, 如果设置了ArgsReply, 则返回Reply和错误
	ReturnNum int
	// 路由组的名字
	GroupName string
	//
	ArgsRefType  string
	ReplyRefType string
	//
	GetContext ContextCreator
	//
	GetRPC ChanRPCSelector
}

func (router *Router) GetEnableHttpGet() bool {
	return router.Config.EnableHttpGet
}

func (router *Router) NewArgs() (args interface{}, err error) {
	desc := reflectx.GetType(router.ArgsRefType)
	if desc != nil {
		args, err = reflectx.NewObject(router.ArgsRefType)
		if err != nil {
			return
		}
	} else {
		if router.Args != nil {
			args = reflect.New(router.Args).Interface()
		} else {
			args = struct{}{}
		}
	}

	return
}

func (router *Router) NewReply() (obj interface{}, err error) {
	desc := reflectx.GetType(router.ReplyRefType)
	if desc != nil {
		obj, err = reflectx.NewObject(router.ReplyRefType)
		if err != nil {
			return
		}
	} else {
		if router.ArgsReply != nil {
			obj = reflect.New(router.ArgsReply).Interface()
		}
	}
	return
}

var callErrorBuf = make([]byte, 1<<20)

func (router *Router) Call(ctx context.Context, args interface{}, result interface{}) (err error) {
	defer func() {
		if !DEBUG_MODE {
			// todo: 写入异常日志
			if errRecover := recover(); errRecover != nil {

				buf := callErrorBuf
				length := runtime.Stack(buf, false)

				str := string(buf[:length-1])

				arr := strings.Split(str, "\n")
				arr = arr[5:]
				var r []string
				var rIndex = -1
				for _, val := range arr {
					if val == "" {
						continue
					}
					if string(val[0]) != "\t" {
						if string(val[len(val)-1]) == ")" {
							lastIndex := strings.LastIndex(val, "(")
							if lastIndex != -1 {
								val = val[:lastIndex]
							}
						}
						if val[:6] == "create" {
							r = append(r, val)
						} else {
							r = append(r, "at "+val)
						}

						rIndex = len(r) - 1
					} else {
						re := regexp.MustCompile(`.+?\.go:\d+`)
						rFind := re.FindAllString(val, -1)
						fileBase := ""
						if len(rFind) > 0 {
							fileBase = filepath.Base(rFind[0])
						}

						if rIndex <= len(r)-1 {
							r[rIndex] += ":" + fileBase
						}
					}
				}
				logx.Error(errorx.New("系统调用异常",
					datax.M{
						"args": fmt.Sprintf("%+v", args),
						"err":  "\n" + fmt.Sprintf("%s", errRecover) + "\n" + strings.Join(r, "\n")}),
				)
				err = errorx.New("系统调用异常")
			}
		}
	}()

	if v, ok := args.(IValid); ok {
		if valerr := v.Valid(); valerr != nil {
			err = errorx.Wrap(valerr, "valid")
			return
		}
	}
	var results []reflect.Value
	if args != nil && result != nil {
		results = router.Config.Handler.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(args), reflect.ValueOf(result)})
	} else if args != nil {
		results = router.Config.Handler.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(args)})
	} else {
		results = router.Config.Handler.Call([]reflect.Value{reflect.ValueOf(ctx)})
	}
	switch len(results) {
	case 0:
	case 1:
		errval := results[0]
		if !errval.IsNil() {
			err = errval.Interface().(error)
			return
		}
	default:
		err = errorx.New("路由处理函数返回的结果数量不能超过两个")
	}
	return
}

func (n *Node) NewRouter(conf *RouterConfig) (router *Router, err error) {
	router = &Router{
		Config: conf,
	}

	ftyp := conf.Handler.Type()
	if ftyp.NumIn() > 1 {
		rtyp1 := ftyp.In(1)
		if rtyp1.Kind() != reflect.Ptr {
			router.Args = nil
		} else {
			router.Args = ftyp.In(1).Elem()
		}

	}
	if ftyp.NumIn() > 2 {
		router.ArgsReply = ftyp.In(2).Elem()
	}

	if router.Args != nil {
		reftyp, hasreftyp := router.Args.MethodByName("RefType")
		if hasreftyp {
			results := reftyp.Func.Call(nil)
			router.ArgsRefType = results[0].String()
		} else {
			pkg := stringutil.SplitLast(router.Args.PkgPath(), "/")
			router.ArgsRefType = pkg + "." + router.Args.Name()
		}
	}

	router.ReturnNum = ftyp.NumOut()

	if conf.Path == "" {
		err = errorx.New("name is null")
		return
	}

	arr := strings.Split(conf.Path, "/")
	if len(arr) > 0 {
		router.GroupName = arr[0]
	}

	switch router.Config.CallMode {
	case CHANRPC:
		_, hasrpc := n.config.ChanRPCSelectors[router.Config.ChanRPC]
		if hasrpc == false {
			err = errorx.New("路由为CHANRPC模式，但RPCSelector没有定义", datax.M{"Router": router.Config.Path, "ChanRPCSelector": router.Config.ChanRPC})
			return
		}
	}

	return

}
